package lion.dev.lang;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.WordUtils;

public class BeanUtil {

	private Object target;
	private Class<?> clazz;

	public BeanUtil(Object target) {

		this.target = target;
		this.clazz = target.getClass();
	}

	public Object exec(String method, Object... params) {

		return exec(this.getMehtod(method, getParamClass(params)), params);
	}

	/**
	 * @param params
	 * @return
	 */
	private Class<?>[] getParamClass(Object[] params) {

		if (params == null) { return null; }

		Class<?>[] results = new Class<?>[params.length];
		for (int i = 0; i < params.length; i++) {
			results[i] = params[i] == null ? null : params[i].getClass();
		}
		return results;
	}

	public Object exec(Method method, Object... params) {

		if (method == null) { return null; }
		Object result = null;
		try {
			method.setAccessible(true);
			result = method.invoke(this.target, params);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	public Method getMehtod(String method, Class<?>... params) {

		Method result = null;
		// 直接获取
		try {
			result = this.clazz.getMethod(method, params);
		} catch (Exception e) {
		}
		if (result != null) { return result; }

		// 猜测方法，同名的方法进行猜测
		// 1、不考虑可变参数的情况
		// 2、方法个数相同 +5
		// 3、子类+5
		List<Method> methods = new ArrayList<>();
		for (Method me : this.clazz.getMethods()) {
			if (me.getName().equals(method)) {
				methods.add(me);
			}
		}
		if (methods.isEmpty()) { return null; }

		int paramlen = params == null ? 0 : params.length;
		int maxscore = 0;
		for (Method m : methods) {
			int score = 0;
			Type[] types = m.getParameterTypes();
			if (types.length == paramlen) {
				score += 5;// 参数个数相同 权重+10
				for (int i = 0; i < paramlen; i++) {
					if (params[i] == null) {
						score += 5;
					} else if (types[i].getClass().equals(params[i])) {
						score += 15;// 参数相同，权重 +10
					} else if (types[i].getClass().isAssignableFrom(params[i])) {
						score += 10; // 参数为子类，权重+5
					}
				}
			}
			if (score > maxscore) {
				maxscore = score;
				result = m;
			}
		}
		return result;
	}

	public boolean hasMethod(String method, Class<?>... params) {

		return this.getMehtod(method, params) != null;
	}

	public boolean hasField(String field) {

		return this.getField(field) != null;
	}

	public Field getField(String field) {

		Field f = null;

		try {
			f = this.clazz.getDeclaredField(field);
		} catch (Exception e) {
		}

		return f;
	}

	public void setValue(String field, Object value) {

		Field f = this.getField(field);
		if (f != null) {
			try {

				f.setAccessible(true);
				f.set(this.target, value);
			} catch (Exception e) {
				e.printStackTrace();
			}
		} else {
			String method = "set" + WordUtils.capitalize(field);
			if (this.hasMethod(method, value.getClass())) {
				this.exec(method, value);
			}
		}
	}

	private void setValue(Field f, Object value) {

		try {

			f.setAccessible(true);
			f.set(this.target, value);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void setValue(MapJ param) {

		for (String key : param.keySet()) {
			this.setValue(key, param.get(key));
		}
	}

	public Object getValue(String field) {

		Field f = this.getField(field);
		Object result = null;
		if (f != null) {
			try {
				f.setAccessible(true);
				result = f.get(this.target);
			} catch (Exception e) {
			}
		}

		return result;

	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static void populate(Object target, MapJ param) {

		BeanUtil bu = new BeanUtil(target);
		for (String key : param.keySet()) {
			Field f = bu.getField(key);
			Object v = param.get(key);
			if (f == null || v == null) {
				continue;
			}

			if (f.getType().isAssignableFrom(v.getClass())) {
				bu.setValue(key, v);
				continue;
			}

			if (f.getType().equals(Boolean.class) || f.getType().equals(Boolean.TYPE)) {
				bu.setValue(f, param.getBoolean(key));
			} else if (Date.class.isAssignableFrom(f.getType())) {
				bu.setValue(f, param.getDate(key));
			} else if (CharSequence.class.isAssignableFrom(f.getClass())) {
				bu.setValue(f, param.getString(key));
			} else if (f.getType().equals(Byte.class) || f.getType().equals(Byte.TYPE)) {
				bu.setValue(f, param.getByte(key));
			} else if (f.getType().equals(Short.class) || f.getType().equals(Short.TYPE)) {
				bu.setValue(f, param.getShort(key));
			} else if (f.getType().equals(Integer.class) || f.getType().equals(Integer.TYPE)) {
				bu.setValue(f, param.getInt(key));
			} else if (f.getType().equals(Long.class) || f.getType().equals(Long.TYPE)) {
				bu.setValue(f, param.getLong(key));
			} else if (f.getType().equals(Float.class) || f.getType().equals(Float.TYPE)) {
				bu.setValue(f, param.getFloat(key));
			} else if (f.getType().equals(Double.class) || f.getType().equals(Double.TYPE)) {
				bu.setValue(f, param.getDouble(key));
			} else if (f.getType().isEnum()) {
				if (v instanceof String) {
					bu.setValue(f, Enum.valueOf((Class<Enum>) f.getType(), (String) v));
				} else if (v.getClass().equals(Integer.class)) {
					bu.setValue(f, f.getType().getEnumConstants()[(Integer) v]);
				}
			} else {
				bu.setValue(f, v);
			}
		}
	}
}
